#include "mainwindow.h"
#include "./ui_mainwindow.h"

#include <opencv2/opencv.hpp>
#include <opencv2/highgui/highgui_c.h>

#include <sys/socket.h>
#include "MyEncoder.h"
#include <stdio.h>                                                         
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <unistd.h>


//Houlc Socket and Encode
int oneUdpPacketSize = 1400;

Socket_Udp_Client_Info * initUdpClient()
{
    Socket_Udp_Client_Info * mSocketInfo =  \
        (Socket_Udp_Client_Info *)malloc(sizeof(Socket_Udp_Client_Info));

    mSocketInfo->sclient = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);

    mSocketInfo->sin.sin_family = AF_INET;
    mSocketInfo->sin.sin_port = htons(8888);
    mSocketInfo->sin.sin_addr.s_addr = inet_addr("127.0.0.1");
    //mSocketInfo->sin.sin_addr.s_addr = inet_addr("192.168.249.135");
    //mSocketInfo->sin.sin_addr.s_addr = inet_addr("192.168.1.100");
    mSocketInfo->len = sizeof(mSocketInfo->sin);

    return mSocketInfo;
}

void unitUdpClient(Socket_Udp_Client_Info * mSocketInfo)
{
    close(mSocketInfo->sclient);
}

//Houlc step 1
void initVideoParamSend(Socket_Udp_Client_Info *sockInfo, 
                        cv::Mat &imageTemp)
{
    int info[3];
    int ret = 0;
    int flag = 0;
    info[0] = imageTemp.cols;
    info[1] = imageTemp.rows;
    info[2] = imageTemp.channels();
    // 先把宽高和通道数传过去
    printf("video size: cols:%ld||rows:%ld||channels:%ld\n",
                            imageTemp.cols, imageTemp.rows, 
                            imageTemp.channels());                         
    
    ret = sendto(
            sockInfo->sclient, (char *)info, sizeof(int) * 3, 0, 
            (sockaddr *)&sockInfo->sin, sockInfo->len);
    if (ret == sizeof(int) * 3)
    {
        printf("client send width height channel succeed \n");
    }
    if (flag == 1)
    {
        printf("client dui fang succeed recevied \n");
    }
}

//Houlc step 2
void doEncodeSendFrame(cv::Mat &imageTemp, 
                       MyEncoder &myencoder,
                       Socket_Udp_Client_Info *sockInfo
                       )
{
    char * ptrAlloc;// = (char *)calloc(sizeof(char), 1000 * 1000 * 3);
    AVPacket pkt; av_init_packet(&pkt); pkt.size=0; pkt.data=NULL; 
    myencoder.Ffmpeg_Encoder_Encode_New(
                                   (uint8_t*)(imageTemp.data), &pkt
                                       );
    if(pkt.size ==0) return ;

    int ret;
    ptrAlloc = (char*)pkt.data;
    if(pkt.size <= oneUdpPacketSize)
    {
        ret = sendto(
                     sockInfo->sclient, (char*)ptrAlloc, pkt.size, 0, 
                     (sockaddr*)&sockInfo->sin, sockInfo->len
                    );
        printf("[DEBUG] [NoUnpacked] send one packet ret = %d \n", ret);
    }else{
        int numCount = pkt.size / oneUdpPacketSize;
        printf("[DEBUG] [Unpacked] packet too big, unpack to <num> %d packets \n");
        int i = 0;
        for (i = 0; i < numCount; i++)
        {
            char * ptr = (ptrAlloc) + i * oneUdpPacketSize;
            ret = sendto(
                         sockInfo->sclient, ptr, oneUdpPacketSize, 0, 
                         (sockaddr*)&sockInfo->sin, sockInfo->len
                        );
            printf("[DEBUG] [Unpacked] send one packet ret = %d \n", ret);
            usleep(1);
        }
        char * ptr = ptrAlloc + i * oneUdpPacketSize;
        int resSize = pkt.size - numCount * oneUdpPacketSize;
        ret = sendto(
                     sockInfo->sclient, ptr, resSize, 0, 
                     (sockaddr*)&sockInfo->sin, sockInfo->len
                    );
    }
    usleep(1);
    av_free_packet(&pkt);
    cv::waitKey(30);
}

//Houlc Qt 

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    //Houlc udp init
    _sockInfo = initUdpClient(); 


    //Houlc Vdieo init

    //cv::VideoCapture capture(0);
    fps = 15;
    video= cv::VideoCapture(0);
    //Houlc capture set
    video.set(CAP_PROP_FRAME_WIDTH, 640);//宽度
    video.set(CAP_PROP_FRAME_HEIGHT, 480);//高度
    video.set(CAP_PROP_FPS, 15);//帧率 帧/秒
    //capture.set(CAP_PROP_BRIGHTNESS, 1);//亮度 
    //capture.set(CAP_PROP_CONTRAST,40);//对比度 40
    //capture.set(CAP_PROP_SATURATION, 50);//饱和度 50
    //capture.set(CAP_PROP_HUE, 50);//色调 50
    //capture.set(CAP_PROP_EXPOSURE, 50);//曝光 50 获取摄像头参数
    //end


    video>>_imageTemp;
    ShowImage(_imageTemp);

    //Send Video params 
    initVideoParamSend(_sockInfo, _imageTemp);


    //Houlc ffmpeg Init
    _myencoder.Ffmpeg_Encoder_Init();
    _myencoder.Ffmpeg_Encoder_Setpara(AV_CODEC_ID_H264,
                                     _imageTemp.cols, _imageTemp.rows);
    printf("Image size: [%d %d]\n", _imageTemp.cols, _imageTemp.rows);


    //Houlc set Button
    connect(ui->pushButton_start, &QPushButton::clicked, [&]() {
        on_pushButton_play_clicked();
    });

    connect(ui->pushButton_pause, &QPushButton::clicked, [&]() {
    on_pushButton_pause_clicked();
    });

    timer=new QTimer(this);
    ui-> label_image->setScaledContents(true);//可以使图片完全按QWidget缩放，而不保持原视频比例
    connect(timer,SIGNAL(timeout()),this,SLOT(ReadFrame()));
}

void MainWindow::ShowImage(cv::Mat& mat)
{
     //方法二，按比例缩放
    QImage image=MatToQImage2(mat);
    QPixmap pixmap = QPixmap::fromImage(image);
    int width = ui->label_image->width();
    int height = ui->label_image->height();
    QPixmap fitpixmap = pixmap.scaled(width, height, 
                Qt::KeepAspectRatio, Qt::SmoothTransformation);
    //按比例缩放
    ui->label_image->setPixmap(fitpixmap);
}

//图像格式转换
QImage MainWindow::MatToQImage2(const cv::Mat &mat)
{
    QImage img;
    int chana = mat.channels();
    //依据通道数不同，改变不同的装换方式
    if(3 == chana ){
        //调整通道次序
        cv::cvtColor(mat,mat,cv::COLOR_BGR2RGB);
        img = QImage(static_cast<uchar *>(mat.data),mat.cols,mat.rows,QImage::Format_RGB888);
    }
    else if(4 == chana )
    {
        //argb
        img = QImage(static_cast<uchar *>(mat.data),mat.cols,mat.rows,QImage::Format_ARGB32);
    }
    else {
    //单通道，灰度图
    img = QImage( mat.cols, mat.rows , QImage::Format_Indexed8);
    uchar * matdata = mat.data;
        for(int row = 0 ; row <mat.rows ; ++row )
        {
            uchar* rowdata = img.scanLine( row );
            memcpy(rowdata,matdata ,mat.cols);
        matdata+=mat.cols;
        }
    }
    return img;
}

MainWindow::~MainWindow()
{
    delete ui;
}


void MainWindow::on_pushButton_play_clicked()
{
    timer->start(1000/fps);
}

void MainWindow::on_pushButton_pause_clicked()
{
    timer->stop();
}



void MainWindow::ReadFrame()
{

    //cv::Mat frame_now;
    video >> _imageTemp;//读帧进frame
    if (_imageTemp.empty())
    {
        //QMessageBox::information(this, tr("warning"), tr("the video is end!"));
        timer->stop();

    }
    else {
        doEncodeSendFrame(_imageTemp, _myencoder, _sockInfo);
        usleep(1);
        ShowImage(_imageTemp);
    }
}
